package com.typhoon.spring_shiro.controller.interceptor;

import java.lang.reflect.Method;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.typhoon.spring_shiro.annotation.Logical;
import com.typhoon.spring_shiro.annotation.RequirePermission;
import com.typhoon.spring_shiro.constants.PermissionConstants;
import com.typhoon.spring_shiro.constants.PermissionEntityError;
import com.typhoon.spring_shiro.utils.redis.RedisKeyUtils;
import com.typhoon.spring_shiro.utils.redis.RedisKeyUtils.Model;
import com.typhoon.spring_shiro.utils.redis.RedisUtils;

/**
 * 权限拦截器
 * 
 * @author Typhoon
 * @date 2017-06-05 20:20
 * @since V2.0
 */
public class PermissionInterceptor extends AbstractCustomInterceptor implements HandlerInterceptor {

	private static final Logger LOGGER = LoggerFactory.getLogger(PermissionInterceptor.class);

	private static final int PERM_VALUE_THRESHOLD = 3;//@RequirePermission中value个数阈值(不能超过3个)

	@SuppressWarnings("unchecked")
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();
		boolean isMethodAnnoted = method.isAnnotationPresent(RequirePermission.class);
		if (!isMethodAnnoted) {
			return true;
		}
		RequirePermission requirePermission = method.getAnnotation(RequirePermission.class);
		if (null == requirePermission || (ArrayUtils.isEmpty(requirePermission.value()))) {
			return true;
		}
		String[] permissions = requirePermission.value();
		if(PERM_VALUE_THRESHOLD < permissions.length) {
			LOGGER.debug("注解value个数不能超过:{},实际为:{}个",PERM_VALUE_THRESHOLD,permissions.length);
			this.putParamResult(response, PermissionEntityError.UNAUTHORIZED_OP);
			return false;
		}
		Logical logical = requirePermission.logical();
		LOGGER.debug("权限码:{},逻辑关系:{}", ArrayUtils.toString(permissions), logical);
		boolean isAnd = false;
		if (Logical.AND.equals(logical)) {// 且
			isAnd = true;
		}

		Long workerId = this.decryptWorkerId(request, response);
		if(null == workerId){
			return false;
		}
		Set<String> apps = null;
		Set<String> menus = null;
		Set<String> actions = null;
		try {
			String key = RedisKeyUtils.generateKey(Model.PERMISSION, workerId, PermissionConstants.APP_PRE);
			apps = RedisUtils.get(key, Set.class);

			key = RedisKeyUtils.generateKey(Model.PERMISSION, workerId, PermissionConstants.MENU_PRE);
			menus = RedisUtils.get(key, Set.class);

			key = RedisKeyUtils.generateKey(Model.PERMISSION, workerId, PermissionConstants.ACTION_PRE);
			actions = RedisUtils.get(key, Set.class);
		} catch (Exception e) {
			LOGGER.error("worerId:"+ workerId + "从redis获取权限信息异常",e);
			return false;
		}
		
		boolean isAollowed = false;
		String pm = null;
		if (isAnd) {
			isAollowed = true;
			for (int i = 0, length = permissions.length; i < length; i++) {
				pm = permissions[i];
				if (pm.startsWith(PermissionConstants.APP_PRE)) {
					if (null == apps || !apps.contains(pm)) {
						isAollowed = false;
						break;
					}
				} else if (pm.startsWith(PermissionConstants.MENU_PRE)) {
					if (null == menus || !menus.contains(pm)) {
						isAollowed = false;
						break;
					}
				} else if (pm.startsWith(PermissionConstants.ACTION_PRE)) {
					if (null == actions || !actions.contains(pm)) {
						isAollowed = false;
						break;
					}
				}
			}
		} else {// 或者
			isAollowed = false;
			for (int i = 0, length = permissions.length; i < length; i++) {
				pm = permissions[i];
				if (pm.startsWith(PermissionConstants.APP_PRE)) {
					if (null != apps && apps.contains(pm)) {
						isAollowed = true;
						break;
					}
				} else if (pm.startsWith(PermissionConstants.MENU_PRE)) {
					if (null != menus && menus.contains(pm)) {
						isAollowed = true;
						break;
					}
				} else if (pm.startsWith(PermissionConstants.ACTION_PRE)) {
					if (null != actions && actions.contains(pm)) {
						isAollowed = true;
						break;
					}
				}
			}
		}
		
		if (!isAollowed) {
			this.putParamResult(response, PermissionEntityError.UNAUTHORIZED_OP);
		}
		return isAollowed;
	}
}
